﻿
namespace Patterns4Net.Tests.Core.CecilExtensions
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using Mono.Cecil;
    using NUnit.Framework;
    using Patterns4Net.Core.CecilExtensions;

    [TestFixture]
    public class GetBaseTypesExtensionTest
    {
        /*
         * According to MSDN Dictionary<TKey, TValue> implements:
         * 
         *      IDictionary<TKey, TValue>,
         *      ICollection<KeyValuePair<TKey, TValue>>, 
         *      IEnumerable<KeyValuePair<TKey, TValue>>, 
         *      IDictionary, 
         *      ICollection, 
         *      IEnumerable, 
         *      ISerializable, 
         *      IDeserializationCallback
         *      
         * and IEnumerable<T> implements:
         * 
         *      IEnumerable
         */

        private const int DictionaryInterfacesCount = 8;

        [Test]
        public void GetBaseTypesAndIFaces_On_MyDictionary_With_Include_IFaces_And_Include_BaseTypes()
        {
            var result = GetBaseTypesExtension.GetBaseTypesAndIFaces(GetTypeDefinition<MyDictionary>(), true, true);

            Assert.AreEqual(DictionaryInterfacesCount + 3, result.Count());
            AssertAny<StrDict<double>>(result);
            AssertAny<Dictionary<string, double>>(result);
            AssertAny<object>(result);
            AssertAny<IDictionary<string, double>>(result);
            AssertAny<IEnumerable>(result);
            AssertAny<IDictionary>(result);
            AssertAny<ICollection>(result);
            AssertAny<IDeserializationCallback>(result);
        }

        [Test]
        public void GetBaseTypesAndIFaces_On_MyDictionary_With_Include_Only_BaseTypes()
        {
            var result = GetBaseTypesExtension.GetBaseTypesAndIFaces(GetTypeDefinition<MyDictionary>(), true, false);

            Assert.AreEqual(3, result.Count());
            AssertAny<StrDict<double>>(result);
            AssertAny<Dictionary<string, double>>(result);
            AssertAny<object>(result);
        }

        [Test]
        public void GetBaseTypesAndIFaces_On_MyDictionary_With_Include_Only_IFaces()
        {
            var result = GetBaseTypesExtension.GetBaseTypesAndIFaces(GetTypeDefinition<MyDictionary>(), false, true);

            Assert.AreEqual(DictionaryInterfacesCount, result.Count());
            AssertAny<IDictionary<string, double>>(result);
            AssertAny<IEnumerable>(result);
            AssertAny<IDictionary>(result);
            AssertAny<ICollection>(result);
            AssertAny<IDeserializationCallback>(result);
        }

        [Test]
        public void GetBaseTypesAndIFaces_On_List_Of_Int_Returns_IList_Of_Int()
        {
            var type = typeof(List<int>).GetTypeReference();
            var result = GetBaseTypesExtension.GetBaseTypesAndIFaces(type, true, true);
            AssertAny<IList<int>>(result);
        }

        private static void AssertAny<T>(IEnumerable<TypeReference> result)
        {
            var message = string.Format("Failed to assert that resulting contains type {0}.", typeof(T).Name);
            Assert.That(result.Any(t => t.IsEqual(typeof(T))), message);
        }

        private static TypeDefinition GetTypeDefinition<T>()
        {
            return typeof(T).GetTypeDefinition();
        }

        private class StrDict<T> : Dictionary<string, T>
        {
        }

        private class MyDictionary : StrDict<double>
        {
        }

        private class MyList<T> : List<T>, IEnumerable<int>
        {
            IEnumerator<int> IEnumerable<int>.GetEnumerator()
            {
                throw new NotImplementedException();
            }
        }
    }
}
